<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="app">
      <button @click="show = !show">点击切换 卸载组件</button>

      <children-page v-if="show" :x="x"></children-page>
    </div>
  </body>
  <script src="../js/vue.js"></script>
  <script>
    const app = Vue.createApp({
      data() {
        return {
          x: 1,
          show: true,
        };
      },
      provide() {
        return {
          msg: "子组件可以依赖的数据",
        };
      },
    });

    app.component("ChildrenPage", {
      name: "ChildrenPage",
      data() {
        return {
          y: 2,
          timer: null,
        };
      },
      props: ["x"],
      inject: ["msg"],
      methods: {
        change() {
          this.y++;
        },
        open() {
          this.timer = setInterval(() => {
            console.log("计时器");
          }, 1000);
        },
      },
      template: `
        <div>我是 Children</div>
        <p ref="p">数据：{{ y }}</p>
        <button @click="change">点击修改数据</button>
        <button @click="open">开启计时器</button>
      `,
      // 创建阶段

      // 初始化 Vue 内部的事件系统,定义生命周期函数,对外部传递到组件内部的数据进行响应式代理处理
      // 在当前阶段 data、methods、computed 以及 watch 上的数据和方法都不能被访问
      beforeCreate() {
        console.log("beforeCreate");
        console.log(this.$props, "外部的数据处理完成");
        console.log(this.msg, "不能获取依赖注入的数据");
        console.log(this.$data, "内部的数据还没有开始处理");
        console.log(this.$el, "组件的标签还没有开始被创建");
      },
      // 对组件内部的 data 数据进行响应式处理,获取注入的数据添加到 this 对象上
      // 这里没有 el，如果非要想与DOM进行交互，可以通过 this.nextTick 来访问 Dom
      created() {
        console.log("created");
        console.log(this.msg, "获取到依赖注入的值");
        console.log(this.$data, "内部的数据响应式处理完成");
        console.log(this.$el, "组件的标签还没有开始被创建");
      },
      // 挂载阶段

      // 相关的 render 函数首次被调用
      beforeMount() {
        console.log("beforeMount");
        console.log("相关的 render 函数首次被调用");
        console.log(this.$el, "组件的标签还没有开始被创建");
      },
      /*
        将创建阶段准备的数据和准备好的模板结合到一起(数据替换和事件注册),生成真正的标签,
        然后根据组件使用的位置,添加到具体的 DOM 树结构中,
        最终的根组件追加到 mount 方法指定的标签中
      */
      mounted() {
        console.log("mounted");
        console.log(this.$el, "组件的标签被创建完成");
        console.log(this.$refs);
      },
      // 更新阶段

      // 只有数据修改导致页面标签修改才成触发更新阶段
      beforeUpdate() {
        console.log("beforeUpdate");
        console.log(this.$data.y, "组件内的数据已经更新完成");
        var p = document.querySelector("p");
        console.log(p.outerHTML, "页面上对应的标签内容还内开始被修改");
        console.log(this.$refs.p);
      },
      // vue 会根据数据变化的范围,结合 diff 算法和 key 标记,对页面上的标签结构进行最小范围的更新
      // 当前阶段组件 Dom 已完成更新,要注意的是避免在此期间更改数据，因为这可能会导致无限循环的更新，该钩子在服务器端渲染期间不被调用。
      updated() {
        console.log("updated");
        var p = document.querySelector("p");
        console.log(p.outerHTML, "页面上对应的标签内容被修改");
      },
      // 销毁阶段

      // 当组件不需要再被使用,从组件树/DOM树中去除时,会触发卸载阶段
      beforeUnmount() {
        console.log("beforeUnmount");
        console.log(this.$data);
        console.log(this.$el);
      },
      // 消除模板上的所有指令和事件绑定,删除页面上实际的标签
      unmounted() {
        console.log("unmounted");
        console.log(this.$data);
        console.log(this.$el);
        // 如果组件内使用的一些全局性方法或内容,就必须在组件卸载时,取消或关闭自己开启的全局内容,消除其他组件的影响
        // 比如,开启计时器,发送 ajax 到组件不使用时都没有响应结果,向 html/body 注册事件函数等
        clearInterval(this.timer);
      },
    });
    app.mount("#app");
  </script>
</html>
